<?php namespace App\Core\Repository;

use DB;
abstract class Sort {
    
    public $repository = null;
    public $model = null;
    
    public $key;
    public $field;
    public $max;
    public $isBooted = false;
    
    public $attrs = [];
    public $criterias = [];
    
    //配置['key' => '主键的名称 默认从模型获取', 'field' => '用于排序的字段 默认sort', 'max' => '限制条数']
    public function __construct($config) {
        $this->key = isset($config['key']) ? $config['key'] : null;
        $this->field = isset($config['field']) ? $config['field'] : 'sort';
        $this->max = isset($config['max']) ? $config['max'] : 10000;
        $this->attrs = isset($config['attrs']) ? $config['attrs'] : [];
        $this->criterias = isset($config['criterias']) ? $config['criterias'] : [];
    }

    //初始化
    public function boot(Repository $repository){
        $this->repository = $repository;
        $this->model = $repository->getModel();
        $this->key = $this->formatField($this->model, ($this->key ? $this->key : $this->model->getKeyName()));
        $this->isBooted = true;
        return $this;
    }

    //追加到队列最后
    public function append($selfId){
        //初始化条件
        foreach($this->criterias as $criteria){
            $criteria->selfId = $selfId;
        }
        $this->validate();
        $selfObj = $this->getQuery()->where($this->key, '=', $selfId)->first();
        if(!$selfObj){
            throw new \Exception('parameter not validated');
        }
        if(!$this->isSorted($selfObj)){
            $selfObj->{$this->field} = $this->getNextCode($selfObj);
            $selfObj->save();
        }
		//乱序处理
		$this->errorProcess($selfObj);
    }
    
    //追加到队列开头
    public function prepend($selfId){
        //初始化条件
        foreach($this->criterias as $criteria){
            $criteria->selfId = $selfId;
        }
        $this->validate();
        $selfObj = $this->getQuery()->where($this->key, '=', $selfId)->first();
        if(!$selfObj){
            throw new \Exception('parameter not validated');
        }
        if(!$this->isSorted($selfObj)){
            $this->getQuery($selfObj)->where($this->field, '<', $this->max)->increment($this->field);
            $selfObj->{$this->field} = '1';
            $selfObj->save();
        }
		//乱序处理
		$this->errorProcess($selfObj);
    }
    
    //从队列中移除
    public function cancel($selfId){
        //初始化条件
        foreach($this->criterias as $criteria){
            $criteria->selfId = $selfId;
        }
        $this->validate();
        $selfObj = $this->getQuery()->where($this->key, '=', $selfId)->first();
        if(!$selfObj){
            throw new \Exception('parameter not validated');
        }
        if($this->isSorted($selfObj)){
            $this->getQuery($selfObj)->where($this->field, '<', $this->max)->where($this->field, '>', $selfObj->{$this->field})->decrement($this->field);
            $selfObj->{$this->field} = $this->max;
            $selfObj->save();
        }
		//乱序处理
		$this->errorProcess($selfObj);
    }
    
    //移动到目标前面
    public function before($selfId, $targetId){
        //初始化条件
        foreach($this->criterias as $criteria){
            $criteria->selfId = $selfId;
        }
        $this->validate();
        $selfObj = $this->getQuery()->where($this->key, '=', $selfId)->first();
        $targetObj = $this->getQuery($selfObj)->where($this->key, '=', $targetId)->first();
        if(empty($selfObj) || empty($targetObj)){
            throw new \Exception('parameter not validated');
        }
        if(!$this->isSorted($selfObj) || !$this->isSorted($targetObj)){
            throw new \Exception('parameter not validated');
        }
        $targetSort = $targetObj->{$this->field};
        $selfSort = $selfObj->{$this->field};
        if($targetSort == $selfSort){
            throw new \Exception('parameter not validated');
        }
        if($targetSort > $selfSort){//下移
            if(($targetSort - $selfSort) != 1){
                $this->getQuery($selfObj)->where($this->field, '>', $selfSort)->where($this->field, '<', $targetSort)->decrement($this->field);
            }
            $selfObj->{$this->field} = $targetSort-1;
            $selfObj->save();
        }elseif($targetSort < $selfSort){//上移
            $this->getQuery($selfObj)->where($this->field, '>=', $targetSort)->where($this->field, '<', $selfSort)->increment($this->field);
            $selfObj->{$this->field} = $targetSort;
            $selfObj->save();
        }
        //乱序处理
		$this->errorProcess($selfObj);
    }
    
    //移动到目标后面
    public function after($selfId, $targetId){
        //初始化条件
        foreach($this->criterias as $criteria){
            $criteria->selfId = $selfId;
        }
        $this->validate();
        $selfObj = $this->getQuery()->where($this->key, '=', $selfId)->first();
        $targetObj = $this->getQuery($selfObj)->where($this->key, '=', $targetId)->first();
        if(empty($selfObj) || empty($targetObj)){
            throw new \Exception('parameter not validated');
        }
        if(!$this->isSorted($selfObj) || !$this->isSorted($targetObj)){
            throw new \Exception('parameter not validated');
        }
        $targetSort = $targetObj->{$this->field};
        $selfSort = $selfObj->{$this->field};
        if($targetSort == $selfSort){
            throw new \Exception('parameter not validated');
        }
        if($targetSort > $selfSort){//下移
            $this->getQuery($selfObj)->where($this->field, '>', $selfSort)->where($this->field, '<=', $targetSort)->decrement($this->field);
            $selfObj->{$this->field} = $targetSort;
            $selfObj->save();
            
        }elseif($targetSort < $selfSort){//上移
            if(($selfSort - $targetSort) != 1){
                $this->getQuery($selfObj)->where($this->field, '>', $targetSort)->where($this->field, '<', $selfSort)->increment($this->field);
            }
            $selfObj->{$this->field} = $targetSort+1;
            $selfObj->save();
        }
        //乱序处理
		$this->errorProcess($selfObj);
    }
    
    //判断是否已经置顶
    protected function isSorted($item){
        return $item->{$this->field} < $this->max;
    }

    //获取当前最大的排序码
    protected function getNextCode($selfObj){
        $maxCode = $this->getMaxCode($selfObj);
        return $maxCode+1;
    }
    
    //获取当前最大的排序码
    protected function getMaxCode($selfObj){
        $maxSortItem = $this->getLastItem($selfObj);
        return $maxSortItem ? $maxSortItem->{$this->field} : 0;
    }
    
    //获取当前最后一条
    protected function getLastItem($selfObj){
        $maxSortItem = $this->getQuery($selfObj)->where($this->field, '<', $this->max)->orderBy($this->field, 'desc')->first();
        return empty($maxSortItem) ? false : $maxSortItem;
    }
    
    //格式化字段名称
    protected function formatField($model, $fieldRaw){
        if(strpos('.', $fieldRaw) === false){
            return $model->getTable().'.'.$fieldRaw;
        }else{
            return $fieldRaw;
        }
    }
	
	//乱序检测并更正
	protected function errorProcess($selfObj){
        $sorteditems = $this->getQuery($selfObj)->where($this->field, '<', $this->max)->orderBy($this->field)->get();
        $rightCode = 1;
        foreach ($sorteditems as $itemObj){
            if($itemObj->{$this->field} != $rightCode){
                $itemObj->{$this->field} = $rightCode;
                $itemObj->save();
            }
            $rightCode++;
        }
	}

    //获取查询条件
    protected function getQuery($model = null){
        $attrCriterias = [];
        if($model){
            $attrCriterias = $this->getAttrCriterias($model);
        }
        $criterias = array_merge($this->criterias, $attrCriterias);
        $this->repository->resetQuery()->resetCriteria()->addCriterias($criterias);
        return $this->repository->applyCriterias()->getQuery();
    }
    
    //获取对齐属性
    protected function getAttrCriterias($model){
        $criterias = [];
        foreach($this->attrs as $attr){
            $criterias[] = new Criterias\FixedCriteria($attr, $model->$attr);
        }
        return $criterias;
    }

    //验证
    protected function validate(){
        if(!$this->isBooted){
            throw new \Exception('parameter not validated');
        }
    }
}